pacenotes_installer\wizard\scene/
game_folder_routes.rs1use windows_sys::Win32::Foundation::{HWND, LPARAM};
6use windows_sys::Win32::UI::Controls as ctl;
7use windows_sys::Win32::UI::WindowsAndMessaging as wm;
8
9pub unsafe fn show_route_buttons(dialog: HWND) {
11 let ready = crate::wizard::page::state(dialog)
12 .and_then(|state| state.game)
13 .is_some();
14 crate::ui::enable(dialog, crate::wizard::control_ids::IDC_S3_DOWNLOAD, ready);
15 crate::ui::enable(dialog, crate::wizard::control_ids::IDC_S3_CONFIGURE, ready);
16 crate::ui::enable(dialog, crate::wizard::control_ids::IDC_S3_RESTORE, ready);
17}
18
19pub unsafe fn paint_main_route(dialog: HWND, lparam: LPARAM) -> isize {
22 let draw = &*(lparam as *const ctl::NMCUSTOMDRAW);
23 if draw.hdr.idFrom != usize::from(crate::wizard::control_ids::IDC_S3_DOWNLOAD) {
24 return 0;
25 }
26 crate::wizard::page::reply(dialog, crate::ui::paint_forward_button(draw))
27}
28
29pub unsafe fn take_route(dialog: HWND, route: crate::wizard::state::Route) {
32 let Some(state) = crate::wizard::page::state(dialog) else {
33 return;
34 };
35 if state.game.is_none() {
36 super::game_folder::warn_no_game(dialog);
37 return;
38 }
39 let folder = std::path::PathBuf::from(&state.folder);
41 if let Err(reason) = pacenotes_installer::engine::check_writable(&folder) {
42 refuse_unwritable(dialog, state, &reason.to_string());
43 return;
44 }
45 state.route = route;
46 attach_log(dialog, state);
48 open_cache(state);
49
50 let target = match route {
51 crate::wizard::state::Route::Download => crate::wizard::control_ids::IDD_S4_DOWNLOAD,
52 crate::wizard::state::Route::Configure => {
53 super::download::load_tree(state, Vec::new());
55 state.catalogue_loaded = false;
57 if !super::configuration::has_something_to_pick(dialog, state) {
58 return;
59 }
60 state.chosen = state.tree.checked_keys();
61 let line = format!(
62 "configuring from what is already here: {} package(s)",
63 state.chosen.len()
64 );
65 super::progress_page::log_line(dialog, state, &line);
66 crate::wizard::control_ids::IDD_S5_CONFIG
67 }
68 crate::wizard::state::Route::Restore => crate::wizard::control_ids::IDD_RESTORE,
70 };
71 wm::SendMessageW(
72 crate::wizard::page::sheet(dialog),
73 ctl::PSM_SETCURSELID,
74 0,
75 target as LPARAM,
76 );
77}
78
79unsafe fn refuse_unwritable(
83 dialog: HWND,
84 state: &mut crate::wizard::state::WizardState,
85 reason: &str,
86) {
87 let line = format!("the game folder cannot be written to: {reason}");
88 super::progress_page::log_line(dialog, state, &line);
89 if crate::elevation::is_elevated() {
90 let text =
91 crate::i18n::load_string_arg(crate::i18n::IDS_S3_NOT_WRITABLE_ADMIN, &state.folder);
92 crate::ui::message_box_warning(dialog, &text);
93 return;
94 }
95 let question = crate::i18n::load_string_arg(crate::i18n::IDS_S3_NOT_WRITABLE, &state.folder);
96 if !crate::ui::confirm(dialog, &question) {
97 return;
98 }
99 let restart = crate::elevation::Restart {
100 language: state.language,
101 folder: state.folder.clone(),
102 secret: state.secret.clone(),
103 };
104 if crate::elevation::restart_elevated(crate::wizard::page::sheet(dialog), &restart) {
105 std::process::exit(0);
107 }
108 super::progress_page::log_line(dialog, state, "restarting as administrator was cancelled");
109}
110
111fn open_cache(state: &mut crate::wizard::state::WizardState) {
114 let root = state.cache_override.clone().unwrap_or_else(|| {
115 pacenotes_installer::cache::dir_for(std::path::Path::new(&state.folder))
116 });
117 state.cache = pacenotes_installer::cache::Cache::open(root);
118 if state.cache_override.is_none() {
120 sweep_cache(state);
121 }
122}
123
124fn sweep_cache(state: &mut crate::wizard::state::WizardState) {
128 let in_use = pacenotes_installer::restore::payloads_in_use(std::path::Path::new(&state.folder));
129 if in_use.is_none() {
130 state
131 .log
132 .line("cache: a record here could not be read, so no download was removed");
133 }
134 let swept = state.cache.sweep(in_use.as_deref());
135 if swept.files > 0 {
136 state.log.line(format!(
137 "cache: removed {} file(s) nothing needs any more, {}",
138 swept.files,
139 pacenotes_installer::size::human(swept.bytes)
140 ));
141 }
142}
143
144unsafe fn attach_log(dialog: HWND, state: &mut crate::wizard::state::WizardState) {
147 let folder = std::path::PathBuf::from(&state.folder);
148 let dir = pacenotes_installer::runlog::log_dir(&folder);
150 if state.log.path().and_then(|path| path.parent()) == Some(dir.as_path()) {
151 return;
152 }
153 let stamp =
154 pacenotes_installer::utc_time::file_stamp(pacenotes_installer::utc_time::unix_now());
155 state
156 .log
157 .attach(pacenotes_installer::runlog::log_path(&folder, &stamp));
158 if let Some(path) = state.log.path() {
159 crate::crash::log_to(path.to_path_buf());
160 }
161 if let Some(game) = state.game {
162 let line = format!("game: {} in {}", game.title(), state.folder);
163 super::progress_page::log_line(dialog, state, &line);
164 }
165}
166
167const VK_RETURN: usize = 0x0D;
168const DLGC_WANTALLKEYS: isize = 0x0004;
169const PATH_SUBCLASS: usize = 1;
170
171pub unsafe fn enter_takes_main_route(dialog: HWND) {
175 let path = wm::GetDlgItem(dialog, i32::from(crate::wizard::control_ids::IDC_S3_PATH));
176 windows_sys::Win32::UI::Shell::SetWindowSubclass(path, Some(path_keys), PATH_SUBCLASS, 0);
177}
178
179unsafe extern "system" fn path_keys(
180 edit: HWND,
181 message: u32,
182 wparam: windows_sys::Win32::Foundation::WPARAM,
183 lparam: LPARAM,
184 _subclass: usize,
185 _reference: usize,
186) -> windows_sys::Win32::Foundation::LRESULT {
187 let enter = wparam == VK_RETURN;
188 match message {
189 wm::WM_GETDLGCODE if enter => {
190 windows_sys::Win32::UI::Shell::DefSubclassProc(edit, message, wparam, lparam)
191 | DLGC_WANTALLKEYS
192 }
193 wm::WM_KEYDOWN if enter => {
194 take_route(wm::GetParent(edit), crate::wizard::state::Route::Download);
195 0
196 }
197 wm::WM_CHAR if enter => 0,
199 _ => windows_sys::Win32::UI::Shell::DefSubclassProc(edit, message, wparam, lparam),
200 }
201}